#region Basic Using
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
#endregion
using Core;
using Core.Mapping;
using Core.Graphics;
using Core.GameObjects;
using Core.GameObjects.Entities;
using System.Collections.ObjectModel;
using System.Collections;
using System.IO;
using SerializationUtilities;
using Core.GameObjects.Items;

namespace Client.World
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class LocalWorld : Microsoft.Xna.Framework.DrawableGameComponent
    {
        TileWorld TileWorld; //TileWorld object: Contains all Zones/Maps

        ArrayList Maps; //Array of current Map Set

        MapEngine MapEngine; //MapEngine: Renders the Maps to screen
        Rectangle ViewableArea; //The Current Viewable area of the world. Used to calculate maps to Load.

        Collection<GameObject> GameObjects; //Collection of current GameObjects
        Player LocalPlayer; //The Local Player

        SpriteBatch SpriteBatch; //SpriteBatch for drawing sprites to screen

        float fInputDelayTime = 0.5f; //Delay between keyboard input
        float fInputDelayTracker = 0.0f; //Current time between input
        string _WorldDirectoryPath = "";


        public LocalWorld(Game game, string worldPath)
            : base(game)
        {
            // TODO: Construct any child components here
            _WorldDirectoryPath = worldPath;
            GameObjects = new Collection<GameObject>();

        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            LocalPlayer = new Player(0, "Peter");
            LocalPlayer.WorldCoordinate = new Point(20, 20);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            SpriteBatch = new SpriteBatch(GraphicsDevice);//Create SpriteBatch for use in drawing sprites
            SetupMapEngine(); //Setup MapEngine with default options

            LoadWorld(); //Load the World from File

            UpdateCurrentViewableArea();
            UpdateMaps();

            LoadPlayer();

            //Load test monster
            //Player Monster = new Player(27, "Monster 1");

            //string CharPath = TileWorld.diCharacters.FullName;

            //Monster.Body = new Item(10, "Body", 1.0f, 1.0f, ItemType.Armor,
            //    new TileSet(0, Texture2D.FromFile(GraphicsDevice, CharPath + @"\Body\monster1.png"),
            //    "", 24, 32, 0, 0));

            //Monster.AddItems();
            //Monster.RotateObject(Rotation.Down);

            //this.GameObjects.Add(Monster);

            base.LoadContent();
        }

        private void LoadPlayer()
        {
            //Load testing TileSets/Body Parts for Player
            //string CharPath = TileWorld.diCharacters.FullName;

            //LocalPlayer.Body = new Item(10, "Body", 1.0f, 1.0f, ItemType.Armor,
            //    new TileSet(0, Texture2D.FromFile(GraphicsDevice, CharPath + @"\Body\defaultbodycolor.png"),
            //    "", 24, 32, 0, 0));

            //LocalPlayer.Body.SetDrawColor(Color.Green);

            //LocalPlayer.Face = new Item(10, "Face", 1.0f, 1.0f, ItemType.Armor,
            //    new TileSet(0, Texture2D.FromFile(GraphicsDevice, CharPath + @"\Face\face1.png"),
            //    "", 24, 32, 0, 0));

            //LocalPlayer.Hair = new Item(10, "Hair", 1.0f, 1.0f, ItemType.Armor,
            //    new TileSet(0, Texture2D.FromFile(GraphicsDevice, CharPath + @"\Hair\whitehair5.png"),
            //    "", 24, 32, 0, 0));

            //LocalPlayer.Hair.SetDrawColor(Color.Blue);

            //LocalPlayer.TempWorn = new Item(10, "Clothes", 1.0f, 1.0f, ItemType.Armor,
            //    new TileSet(0, Texture2D.FromFile(GraphicsDevice, CharPath + @"\Back\whitecloak.png"),
            //    "", 24, 32, 0, 0));

            //LocalPlayer.TempWorn.SetDrawColor(Color.DarkRed);

            //LocalPlayer.AddItems();

            //GameObjects.Add(LocalPlayer);
        }

        #region WorldLoading
        /// <summary>
        /// Load World from File
        /// </summary>
        public void LoadWorld()
        {
            TileWorld = TileWorld.LoadWorld(_WorldDirectoryPath);
            //Load TileSets
            //Load Terrain TileSets from file
            TileSetLoaderHelper.LoadTileSets(TileWorld.diTextures.FullName, TileSetTypes.Terrain, GraphicsDevice);
        }

        #endregion

        //On KeyDown
        public void KeyDown(KeyboardState args)
        {
            //if enough time has passed since the last input
            if (fInputDelayTracker >= fInputDelayTime)
            {
                //if the map is not currently scrolling
                if (MapEngine.MapSubX == 0 && MapEngine.MapSubY == 0)
                {
                    //Characters current position
                    Point worldC = new Point(LocalPlayer.WorldCoordinate.X, LocalPlayer.WorldCoordinate.Y);

                    if (args.IsKeyDown(Keys.Up))
                    {
                        //Check the tile above the character
                        worldC.Y--;
                        LocalPlayer.RotateObject(Rotation.Up);
                        if (TileWorld.IsTileWalkable(worldC))
                        {
                            //MapEngine.Scroll(Direction.Up);                            
                            LocalPlayer.Move(Direction.Up);
                        }
                        fInputDelayTracker = 0.0f;
                    }

                    if (args.IsKeyDown(Keys.Down))
                    {
                        worldC.Y++;
                        LocalPlayer.RotateObject(Rotation.Down);
                        if (TileWorld.IsTileWalkable(worldC))
                        {
                            //MapEngine.Scroll(Direction.Down);
                            LocalPlayer.Move(Direction.Down);
                        }
                        fInputDelayTracker = 0.0f;
                    }

                    if (args.IsKeyDown(Keys.Left))
                    {
                        worldC.X--;
                        LocalPlayer.RotateObject(Rotation.Left);
                        if (TileWorld.IsTileWalkable(worldC))
                        {
                            //MapEngine.Scroll(Direction.Left);
                            LocalPlayer.Move(Direction.Left);

                        }
                        fInputDelayTracker = 0.0f;
                    }

                    if (args.IsKeyDown(Keys.Right))
                    {
                        worldC.X++;
                        LocalPlayer.RotateObject(Rotation.Right);
                        if (TileWorld.IsTileWalkable(worldC))
                        {
                            //MapEngine.Scroll(Direction.Right);
                            LocalPlayer.Move(Direction.Right);

                        }
                        fInputDelayTracker = 0.0f;
                    }
                }
            }
        }

        //On MouseDown
        public void MouseDown(MouseState buttons)
        {
            Point m = new Point(buttons.X / 32, buttons.Y / 32);
            //Convert mouse location to world coordinate
            m.X += ViewableArea.X;
            m.Y += ViewableArea.Y;
            //Get GameObject at location
            //GameObject go = TileWorld.GetGameObjectAtLocation(m);
            //Execute default action.                        
        }

        public override void Update(GameTime gameTime)
        {
            //Call MapEngines Update
            MapEngine.Update(gameTime);
            //Update time elapsed for input
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            fInputDelayTracker += elapsed;

            //Run Update call for all game Objects
            foreach (GameObject go in GameObjects)
            {
                go.Update(gameTime);
            }

            //Path Finding Test
            //if (fInputDelayTracker >= fInputDelayTime)
            //{
            //    //Check Click Move
            //    if (pf != null)
            //    {
            //        Node ml = pf.GetNextNode();

            //        if (ml != null)
            //        {
            //            if (ml.WorldCoordinate.X == LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y < LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.Up);
            //            else if (ml.WorldCoordinate.X == LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y > LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.Down);
            //            else if (ml.WorldCoordinate.X > LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y == LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.Right);
            //            else if (ml.WorldCoordinate.X < LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y == LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.Left);
            //            else if (ml.WorldCoordinate.X > LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y < LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.UpRight);
            //            else if (ml.WorldCoordinate.X > LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y > LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.DownRight);
            //            else if (ml.WorldCoordinate.X < LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y < LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.UpLeft);
            //            else if (ml.WorldCoordinate.X < LocalPlayer.WorldCoordinate.X && ml.WorldCoordinate.Y > LocalPlayer.WorldCoordinate.Y)
            //                LocalPlayer.Move(Direction.DownLeft);
            //        }
            //    }
            //}

            //Check to see if the map is scrolling.  If the map is scrolling then enable our player's animations
            if (MapEngine.MapSubX != 0 || MapEngine.MapSubY != 0)
            {
                LocalPlayer.SetActivity(Activity.Moving);
            }
            else
            {
                LocalPlayer.SetActivity(Activity.NotMoving);
            }

            base.Update(gameTime);

            //Update ViewableArea when player moves, also check Maps to make sure we have current set.
            UpdateCurrentViewableArea();
            UpdateMaps();
        }

        //Calculate Current Viewable area of world
        protected void UpdateCurrentViewableArea()
        {
            Rectangle view = new Rectangle(
                LocalPlayer.WorldCoordinate.X - MapEngine.MapCenterOffset.X,
                LocalPlayer.WorldCoordinate.Y - MapEngine.MapCenterOffset.Y,
                MapEngine.Width, MapEngine.Height);

            ViewableArea = view;
        }

        //Update Maps for VieableArea
        protected void UpdateMaps()
        {
            Maps = TileWorld.GetMapsForRectangle(ViewableArea);
        }

        public override void Draw(GameTime gameTime)
        {
            //The map x and y to draw starting from, in world coordinates;
            //This takes into account the fact that the character is offset from the edge, 
            Point mapStart = new Point(LocalPlayer.WorldCoordinate.X - MapEngine.MapCenterOffset.X,
                                        LocalPlayer.WorldCoordinate.Y - MapEngine.MapCenterOffset.Y);
            //---------------Draw Base Layer----------------
            //Draw base layer first
            MapEngine.DrawMap(MapLayerBaseTypes.Base, SpriteBatch, ViewableArea, Maps);

            //---------------Draw Lower Layers---------------
            MapEngine.DrawMap(MapLayerBaseTypes.Terrain, SpriteBatch, ViewableArea, Maps);

            //---------------Draw Middle Layers--------------
            DrawBeings(mapStart);

            //----------------Draw Upper Layer---------------
            //MapEngine.DrawMap(MapLayerBaseTypes.Roof, SpriteBatch, ViewableArea, Maps);

            //----------------Draw Overlays------------------
            //Overlay.Draw(_spriteBatch, new Rectangle(_MapEngine.Left, _MapEngine.Top, _MapEngine.Width * 32, _MapEngine.Height * 32));

            base.Draw(gameTime);
        }

        protected void DrawBeings(Point mapS)
        {
            //By subtracting the the current mapx and map y from the beings coordinates we will know where to
            //  draw each of them.  We also know they are all 32 px wide.
            foreach (GameObject go in GameObjects)
            {
                if (go is DrawableGameObject)
                {
                    DrawableGameObject dgo = (DrawableGameObject)go;
                    //if this game object is in our viewable area then draw it
                    //if (IsPointInViewableArea(go.WorldCoordinate))
                    //{
                    //Get objects location in pixels based upon the map offset and the objects current location.
                    //  Take into account left and top offsets as well.
                    int x = (go.WorldCoordinate.X - mapS.X) * 32 + MapEngine.Left;
                    int y = (go.WorldCoordinate.Y - mapS.Y) * 32 + MapEngine.Top;

                    //Draw the object
                    dgo.Draw(SpriteBatch, new Point(x,y));
                    //}
                }
            }
        }

        //Checks to see if a point is inside the current viewable area. This is used for choosing whether to draw a being or not.
        protected bool IsPointInViewableArea(Point pt)
        {
            if (pt.X >= ViewableArea.X && pt.Y >= ViewableArea.Y &&
                pt.X < (ViewableArea.X + ViewableArea.Width) &&
                pt.Y < (ViewableArea.Y + ViewableArea.Height))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #region Maps
        protected void SetupMapEngine()
        {
            MapEngine = new MapEngine();
            //Offset the display by one tiles width and height respectively so that 
            //  when we scroll we don't see the edge of the map
            MapEngine.Left = -32;
            MapEngine.Top = -32;
            //For this same reason we are also over drawing the bottom and right edges, so that
            // when we scroll you don't see the edges of the drawn map, thats why we add +1 and +2 respectively.
            MapEngine.Width = (GraphicsDevice.PresentationParameters.BackBufferWidth / 32) + 1;
            MapEngine.Height = (GraphicsDevice.PresentationParameters.BackBufferHeight / 32) + 2;
            MapEngine.Enable = true;
            MapEngine.UpdateMapCenterOffset();
        }
        #endregion
    }
}